package com.itheima.controller;

import com.alibaba.fastjson.JSON;
import com.itheima.domain.Order;
import com.itheima.domain.Product;
import com.itheima.service.OrderService;
import com.itheima.service.feignClient.ProductServiceClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Random;

/**
 * @ClassName UserController
 * @Description TODO
 * @Date 2023/7/24 15:42
 * @Version 1.0
 */
//@RestController
@Slf4j
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private OrderService orderService;

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private ProductServiceClient productServiceClient;

    /*
        硬编码: 调用商品微服务，查询商品信息，存在的问题:
        1.一旦服务提供者的地址信息变化了,我们就不得不去修改服务调用者的代码
        2.一旦无法提供者做了集群，服务调用者一方无法实现负载均衡去调用
        3.一旦微服务变得越来越多,如何来管理这个服务清单就成了问题
    */
    /*@RequestMapping("/order/prod/{pid}")
    public Order doOrder(@PathVariable("pid") Integer pid) {
        log.info("接收到 {} 号商品的下单请求，接下来调用商品微服务查询此商品信息", pid);
        Product product = restTemplate.getForObject("http://localhost:8081/product/" + pid,
                Product.class);
        log.info("查询到 {} 号商品的信息，内容是: {}", pid, JSON.toJSONString(product));
        //下单(创建订单)
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);

        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }*/

    /*
        只选择第一个服务，存在的问题：
        如果是集群，那么其余的服务永远访问不到
    */
    /*@RequestMapping("/order/prod/{pid}")
    public Order doOrder(@PathVariable("pid") Integer pid) {
        log.info("接收到 {} 号商品的下单请求，接下来调用商品微服务查询此商品信息", pid);

        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
        ServiceInstance instance = instances.get(0);
        Product product = restTemplate.getForObject("http://" + instance.getHost() + ":"
                + instance.getPort() + "/product/" + pid, Product.class);

        log.info("查询到 {} 号商品的信息，内容是: {}", pid, JSON.toJSONString(product));
        //下单(创建订单)
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);

        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }*/

    /*
        负载均衡策略: 自定义随机
     */
    /*@RequestMapping("/order/prod/{pid}")
    public Order doOrder(@PathVariable("pid") Integer pid) {
        log.info("接收到 {} 号商品的下单请求，接下来调用商品微服务查询此商品信息", pid);

        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");

        //随机选择
        int index = new Random().nextInt(instances.size()); //0 1 2
        ServiceInstance instance = instances.get(index);
        Product product = restTemplate.getForObject("http://" + instance.getHost() + ":"
                + instance.getPort() + "/product/" + pid, Product.class);

        log.info("查询到 {} 号商品的信息，内容是: {}", pid, JSON.toJSONString(product));
        //下单(创建订单)
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);

        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }*/

    /*
        负载均衡策略: 使用 Ribbon
        存在的问题：
        1.代码可读性不好
        2.编程风格不统一
     */
    /*@RequestMapping("/order/prod/{pid}")
    public Order doOrder(@PathVariable("pid") Integer pid) {
        log.info("接收到 {} 号商品的下单请求，接下来调用商品微服务查询此商品信息", pid);

        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");

        //直接使用微服务名字， 从nacos中获取服务地址
        String url = "service-product";
        //通过 restTemplate 调用商品微服务
        Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);

        log.info("查询到 {} 号商品的信息，内容是: {}", pid, JSON.toJSONString(product));
        //下单(创建订单)
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);

        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }*/

    /*
        feign
     */
    @RequestMapping("/order/prod/{pid}")
    public Order doOrder(@PathVariable("pid") Integer pid) {
        log.info("接收到 {} 号商品的下单请求，接下来调用商品微服务查询此商品信息", pid);

        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");

        Product product = productServiceClient.findByPid(pid);

        if (product.getPid() == -100) {
            Order order = new Order();
            order.setOid(-100L);
            order.setPname("下单失败");
            return order;
        }

        log.info("查询到 {} 号商品的信息，内容是: {}", pid, JSON.toJSONString(product));
        //下单(创建订单)
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);

        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }

}
